package com.googlecode.h0v3rk4rt.net;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.BindException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.SocketTimeoutException;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ScrollPaneConstants;

import com.googlecode.h0v3rk4rt.util.GameError;

//This has been tested on a wireless network with favorable results. :)

public class MultiplayerServer extends Thread {

	public static final int TCP_PORT = 40315;
	public static final int UDP_PORT = 40316; //+offset
	public static final int MAX_CLIENTS = 2;
	public static final int radius = 15; //for collision detection

	private ServerSocket tcpServer;
	private int clientsConnected;
	private Player p[] = new Player[MAX_CLIENTS];
	private ServerInfoUpdater serverInfoUpdater;
	private boolean shutdown = false;
	public boolean gamestarted = false;

	private JFrame display;
	private JTextArea statusArea;
	private JTextArea messageArea;
	private JScrollPane statusPane;
	private JScrollPane messagePane;

	public static void main(String... args) {
		MultiplayerServer test = new MultiplayerServer();
		test.start();
	}
	
	private double x[][] = new double[][] {{-100,-100},{0,0}};
	private double v[][] = new double[][] {{-900,-900},{0,0}};
	private long time[] = new long[2];
	//This method is called when the server receives an array of doubles dArray from player id
	public void process(double[] dArray, int id) {
		p[1-id].send(dArray); //send these to the other player
		//Collision detection:
		long t = Double.doubleToRawLongBits(dArray[4]);
		if(t <= time[id]) return;
		time[id] = t;
		x[id][0] = dArray[0];
		x[id][1] = dArray[1];
		v[id][0] = dArray[2]*Math.cos(dArray[3]);
		v[id][1] = dArray[3]*Math.sin(dArray[4]);
		if(Math.hypot(x[0][0]-x[1][0], x[0][1]-x[1][1])<radius) {
			double mx = (x[0][0]+x[1][0])/2;
			double my = (x[0][1]+x[1][1])/2;
			for(int i = 0; i < 2; i++) {
				double nx = mx-x[i][0];
				double ny = my-x[i][1];
				nx/=Math.hypot(nx, ny);
				ny/=Math.hypot(nx, ny);
				double vdotn = v[i][0]*nx+v[i][1]*ny;
				v[i][0] -= 2*vdotn*nx; //this is what some call vector math
				v[i][1] -= 2*vdotn*ny;
				x[i][0] -= nx*7.51; //to prevent collisions from happening multiple times
				x[i][1] -= ny*7.51;
			}
			p[0].sendVec(x[0], v[0], -t);
			p[1].sendVec(x[1], v[1], -t);
		}
	}

	public void run() {
		shutdown = false;
		gamestarted = false;
		try {
			display = new JFrame();
			Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
			Dimension frameSize = new Dimension((int)(screenSize.width / 2), (int)(screenSize.height / 2));
			display.setBounds(frameSize.width / 2, frameSize.height / 2, frameSize.width, frameSize.height);
			display.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
			display.addWindowListener(new WindowAdapter() {
				public void windowClosing(WindowEvent arg0) {
					shutdown();
				}
			});
			statusArea = new JTextArea();
			statusArea.setEditable(false);
			statusArea.setFont(new Font("Courier New", Font.PLAIN, 13));
			statusArea.setBackground(Color.black);
			statusArea.setForeground(Color.green);
			statusArea.setLineWrap(true);
			GridLayout gL = new GridLayout(2, 1, 0, 5);
			display.getContentPane().setLayout(gL);
			statusPane = new JScrollPane(statusArea, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
			display.add(statusPane);
			messageArea = new JTextArea();
			messageArea.setEditable(false);
			messageArea.setFont(new Font("Courier New", Font.PLAIN, 13));
			messageArea.setBackground(Color.black);
			messageArea.setForeground(Color.green);
			messageArea.setLineWrap(true);
			messagePane = new JScrollPane(messageArea, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
			display.add(messagePane);
			// For window transparency - Java 1.6+
			// com.sun.awt.AWTUtilities.setWindowOpacity(display, 0.95f);
			display.setVisible(true);
			serverInfoUpdater = new ServerInfoUpdater();
			serverInfoUpdater.start();
			// Initialize TCP server socket
			tcpServer = new ServerSocket(TCP_PORT, MAX_CLIENTS);
			output("Server initialized at IP " + InetAddress.getLocalHost() + " and port " + TCP_PORT);
			output("Waiting for connections...");
			// to allow graceful shutdown
			tcpServer.setSoTimeout(200);
			// Stage 1: Get the clients to log in
			while(!shutdown) {
				try {
					if(clientsConnected == MAX_CLIENTS) break;
					p[clientsConnected] = new Player(clientsConnected, tcpServer.accept(), this);
					clientsConnected++;
				} catch(SocketTimeoutException e) {}
			}
			//Stage 2: Get the game to start
			while(!shutdown && !gamestarted) {
				try { Thread.sleep(200); }
				catch(InterruptedException e) { e.printStackTrace(); }
				if(p[0].loggedIn() && p[1].loggedIn()) {
					p[0].joinGame();
					p[1].joinGame();
					gamestarted = true;
				}
			}
			//Stage 3: Do nothing
			while(!shutdown) {
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//Stage 4: Shut Down
			tcpServer.close();
			output("Server shut down successfully. Exiting...");
			System.exit(0);
		} catch (BindException be) {
			GameError.showErrorDialog(be, "The ports " + TCP_PORT + " or " + UDP_PORT + " are already in use on this machine!", null);
		} catch (IOException e) {
			GameError.showErrorDialog(e);
		}

	}

	//Updates the console
	private class ServerInfoUpdater extends Thread {
		public void run() {
			while(!shutdown) {
				try {
					sleep(200);
				} catch (InterruptedException e) {
					GameError.showErrorDialog(e);
				}
				
				statusArea.setText("IDs\t\tIPs\n");
				for(int i = 0; i < clientsConnected; i++)
					statusArea.append(p[i].toString());
			}
		}
	}
	
	//Shuts down the server and the connected clients
	public void shutdown() {
		output("Shutting down clients...");
		for(int i = 0; i < clientsConnected; i++) p[i].shutdown();
		shutdown = true; //shut down the server
	}

	public void output(String message) {
		messageArea.append(message + "\n");
	}
	
	int winner = -1;	
	public void processWin(int idn) {
		if(winner == -1) winner = idn;
		p[idn].sendTCP("win");
		p[idn].sendTCP("lose");
	}
}
